//factory, tiny version
//$Id: factory.h 272 2009-11-19 19:12:03Z Oleg.Bulychov $
#pragma once

#include <vector>
#include <algorithm>
#include <functional>

#include "src/heo/include/ascii_strcmp.h"
#include "src/heo/include/singleton.h"
#include "src/heo/include/type_util.h"
#include "src/heo/include/reflection.h"
#include "src/heo/include/ptr_visit.h"
//__________________________________________________________________________________
//__________________________________________________________________________________
template<typename I, class V, class C>
class factory_container
{
public:
    typedef I TKey;
    typedef V TValue;
    typedef C TPredicate;

    struct TIdVal
    {
        I   id_;
        V   val_;
    };

    struct TIdCompare:
        public std::binary_function<TIdVal, TIdVal, bool>,
        public C
    {
        bool operator()(TIdVal const& left, TIdVal const& right) const
        {
            return C()(left.id_, right.id_);
        }
        bool operator()(I const& left, TIdVal const& right) const
        {
            return C()(left, right.id_);
        }
        bool operator()(TIdVal const& left, I const& right) const
        {
            return C()(left.id_, right);
        }
    };

    typedef std::vector<TIdVal> TContainer;
    typedef typename TContainer::iterator TIterator;
//.............................................................................
    TContainer vec_;
//.............................................................................
    explicit factory_container(): vec_()
    {}
//.............................................................................
    bool reg(I const& id, V const& v)
    {
        TIterator it = std::lower_bound(vec_.begin(), vec_.end(), id, TIdCompare());
        if (it == vec_.end() || C()(id, it->id_))
        {
            TIdVal const idv = {id, v};
            vec_.insert(it, idv);
            return true;
        }
        return false;
    }
//.............................................................................
    V* get(I const& id)
    {
        TIterator const it = std::lower_bound(vec_.begin(), vec_.end(), id, TIdCompare());
        if (it == vec_.end() || C()(id, it->id_))
            return NULL;
        return &it->val_;
    }
//.............................................................................
};
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
template
<
    class P,
    typename I,
    class C = std::less<I>,
    typename Q = P* (*)()
>
class generic_factory:
    protected factory_container<I, Q, C>
{
    typedef factory_container<I, Q, C> Base_;
public:
    typedef P TObject;
    typedef I TKey;
    typedef C TPredicate;
    typedef Q TProductCreator;
//.............................................................................
    bool register_Creator(I const& id, TProductCreator const& creator)
    {
        ASSERT_(creator);
        bool b = Base_::reg(id, creator);
        ASSERT_(b);
        return b;
    }
//.............................................................................
    TObject* create_Object(I const & id)
    {
        TProductCreator const* creator = Base_::get(id);
        //ASSERT_(creator);
        if (!creator)
            return NULL;
        return (*creator)();
    }
};
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
struct cstr_less:
    public std::binary_function<char const*, char const*, bool>
{
    bool operator()(char const* left, char const* right) const
    {
        return ascii_strcmp(left, right) < 0;
    }
};
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
template<class T>
class factory:
    public singleton< factory<T> >,
    public generic_factory<T, char const*, cstr_less>
{
public:
    explicit factory()
    {}
};
//__________________________________________________________________________________
//__________________________________________________________________________________
namespace hidden
{

template <class Base, class C>
inline Base* new_Class() { return new C; }

template<class T, class I>
class factory_register:
    public singleton< factory_register<T, I> >
{
public:
    explicit factory_register()
    {
        factory<I>::instance().register_Creator(T::get_Name(), new_Class<I, T>);
    }

    void reg()
    {}
};
//__________________________________________________________________________________
}
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
template<class T, class I>
struct factory_object:
    public I
{
    static void instantiate()
    {
        hidden::factory_register<T, I>::instance().reg();
        //:doesn't work in gcc
        //hidden::instantiate_ptr_visit((T*)0, 0, hidden::adl_tag());
    }
};
//__________________________________________________________________________________
//__________________________________________________________________________________

//EOF!
